import { MailSocket } from './MailSocket';
import { Response, ResponseType } from './Response';

export class Client {
  private socket: MailSocket;
  // eventId 区分不同命令返回结果
  private messageHandlersIds: Set<number>;
  // socket数据缓冲区
  private cachedArrayData: string[];
  // sendCommand缓存resolve函数
  private cachedResolveMap: Map<string, Function>;

  public constructor() {
    this.socket = new MailSocket();
    this.messageHandlersIds = new Set();
    this.cachedArrayData = [];
    this.cachedResolveMap = new Map();
    // 监听socket收到数据
    this.socket.setMessageListener('mail-client-internal', { fn: this.onReceiveData.bind(this) });
  }

  // 向上暴露
  public getMailSocket() {
    return this.socket;
  }

  // 生成唯一消息id
  private generateEventId() {
    for (let i = 0; i < 999_999; i++) {
      if (!this.messageHandlersIds.has(i)) {
        this.messageHandlersIds.add(i);
        return i;
      }
    }
  }

  // 连接邮箱服务器
  public connect(host: string, port: number, useTLS?: boolean) {
    return this.socket.connect(host, port, useTLS);
  }

  // 关闭连接
  public close() {
    this.socket.close();
  }

  // 向邮箱服务器发送可标识命令
  public sendCommand(cmd: string, timeout = 20_000) {
    return new Promise<ResponseType<string>>((resolve) => {
      const eventId = `ID${this.generateEventId()}`;
      this.cachedResolveMap.set(eventId, resolve);
      cmd = `${eventId} ${cmd}\r\n`;
      this.socket.send(cmd);
      // 命令超时
      const timeHandler = setTimeout(() => {
        const resolveFn = this.cachedResolveMap.get(eventId);
        this.cachedResolveMap.delete(eventId);
        resolveFn?.(Response(207, `timeout with cmd:${cmd}`));
      }, timeout);
    });
  }

  // 监听socket onData事件
  private onReceiveData(bufferFrame: Buffer) {
    const data = bufferFrame.toString();
    const matches = [...data.matchAll(/(?<id>ID\d+)\s(?<result>BAD|OK).*?\r?\n/g)];
    if (matches.length) {
      matches.forEach((match, i) => {
        const lastMatch: RegExpMatchArray | undefined = matches[i - 1];
        const from = (lastMatch?.index ?? 0) + (lastMatch?.[0].length ?? 0);
        const to = match.index! + match[0].length;
        const prev = data.substring(from, to);
        const last = data.substring(to);
        this.cachedArrayData.push(prev);
        let fullData = this.cachedArrayData.join('');
        this.cachedArrayData.splice(0, this.cachedArrayData.length);
        // 如果此data只匹配到一个有效id返回值，或者处于最后一个，那么后半部分需要缓存起来给下一个使用
        if (i === matches.length - 1 && last) {
          this.cachedArrayData.push(last);
        }
        const taskId = match.groups!['id'];
        const resolveFn = this.cachedResolveMap.get(taskId);
        resolveFn?.(Response(200, 'sendCommand success', fullData));
        this.cachedResolveMap.delete(taskId);
      });
    } else {
      this.cachedArrayData.push(data);
    }
  }
}
